﻿
namespace imichaelmiers.wp7.security.cryptography.PasswordProtectedStorage
{
    using System;
    using System.Security.Cryptography;

    /// <summary>
    /// Internal class containing message authentication and encryption keys
    /// and the PublicCryptographicMetaData associated with it.
    /// </summary>
    internal class SymmetricKeys : IDisposable
    {
        public readonly PublicCryptographicMetaData MetaData;

        private const string NullPasswordMessage = "Password cannot be null.";

        private byte[] macKey;

        private byte[] encryptionKey;

        public SymmetricKeys(string password) :
            this(password, new PublicCryptographicMetaData()) { }

        public SymmetricKeys(byte[] password) :
            this(password, new PublicCryptographicMetaData()) { }

        public SymmetricKeys(string password, PublicCryptographicMetaData meta)
        {
            if (null == password)
            {
                throw new ArgumentNullException(NullPasswordMessage);
            }

            MetaData = meta;
            byte[] passwordAsBytes =
                (new System.Text.UTF8Encoding()).GetBytes(password);
            try
            {
                SetUp(passwordAsBytes);
            }
            finally
            {
                // Erase password. 
                Array.Clear(passwordAsBytes, 0, passwordAsBytes.Length);
            }
        }

        public SymmetricKeys(byte[] password, PublicCryptographicMetaData meta)
        {
            if (null == password)
            {
                throw new ArgumentNullException(NullPasswordMessage);
            }

            MetaData = meta;
            SetUp(password);
        }

        public void SetUp(byte[] password)
        {
            Rfc2898DeriveBytes forMac =
                new Rfc2898DeriveBytes(password, MetaData.SaltForMacKey, 1000);
            Rfc2898DeriveBytes forEnc =
                new Rfc2898DeriveBytes(password, MetaData.SaltForEncryptionKey, 1000);

            macKey = forMac.GetBytes(PublicCryptographicMetaData.KeySizeInBytes);
            encryptionKey = forEnc.GetBytes(PublicCryptographicMetaData.KeySizeInBytes);
        }

        public void SetMacKey(HMAC hmac)
        {
            hmac.Key = macKey;
        }

        public void SetEncryptionKey(SymmetricAlgorithm cipher)
        {
            cipher.Key = encryptionKey;
        }

        void IDisposable.Dispose()
        {
            // Erase keys.
            Array.Clear(macKey, 0, macKey.Length);
            Array.Clear(encryptionKey, 0, encryptionKey.Length);
        }
    }
}
